#include <QtGui>
#include "serialconsole.h"

#if defined(Q_WS_WIN)
#include <windows.h>
#define BUFFERLEN 16
#else
#define BUFFERLEN 512
#endif

SerialConsole::SerialConsole(QWidget *parent) : QPlainTextEdit(parent)
{
    pcmd = PCMD_NONE;
    newline = 13;
    creturn = 10;
    lastchar = 0;
    this->port = NULL;
    this->setMaximumBlockCount(2048);
}

SerialConsole::~SerialConsole()
{
    this->port = NULL;
}

void SerialConsole::setPort(SerialPort *port)
{
    this->port = port;
}

/*
enableClearScreen;
enableHomeCursor;
enablePosXYCursor;
enableMoveCursorLeft;
enableMoveCursorRight;
enableMoveCursorUp;
enableMoveCursorDown;
enableBeepSpeaker;
enableBackspace;
enableTab;
enableCReturn;
enableClearToEOL;
enableClearLinesBelow;
enableNewLine;
enablePosCursorX;
enablePosCursorY;
*/

void SerialConsole::update(int handle)
{
    char buffer[BUFFERLEN];

    if(handle != port->getHandle())
        return;

#if defined(Q_WS_WIN)
    DWORD len = 0;
    int rc = ::ReadFile((HANDLE) handle, buffer, BUFFERLEN, &len, NULL);
    if (rc == 0) {
        qDebug() << "Bad read";
        port->close();
        return;
    }
#else
    int len = ::read(handle, buffer, BUFFERLEN);
    if(len < 0) {
        qDebug() << "Bad read" << len;
        return;
    }
    else if (len == 0) {
        port->close();
        return;
    }
#endif

    for(int n = 0; n < len; n++)
        update(buffer[n]);
}

void SerialConsole::update(QByteArray *barry)
{
    int len = barry->length();
    while(len > 0) {
        update(barry->at(0));
        port->mutex.lock();
        barry->remove(0,1);
        len = barry->length();
        port->mutex.unlock();
    }
}

void SerialConsole::update()
{
    if(port == NULL)
        return;

    char buffer[BUFFERLEN];
    int length = this->port->read(buffer, BUFFERLEN);
    if(length < 1)
        return;

    for(int n = 0; n < length; n++)
        update(buffer[n]);
}

void SerialConsole::update(char ch)
{
    QString text = "";
    QTextCursor cur = this->textCursor();

    bool utfparse = false;
    int  utfbytes = 0;
    int  utf8 = 0;

    if(cur.block().length() > 120)
        cur.insertBlock();

    // always start at the end just in case someone clicked the window
    moveCursor(QTextCursor::End);

    {

        //qDebug(QString(" %1 %2").arg(ch, 2, 16, QChar('0')).arg(QChar(ch)).toAscii());
        //insertPlainText(QString(" %1 ").arg(ch, 2, 16, QChar('0')));
        //insertPlainText(QChar(ch));

        if (ch & 0x80) {    //UTF-8 parsing and handling
            if (utfparse == true) {

                utf8 <<= 6;
                utf8 |= (ch & 0x3F);

                utfbytes--;

                if (utfbytes == 0) {
                    utfparse = false;
                    cur.insertText(QChar(utf8));
                }
            } else {
                utfparse = true;
                utf8 = 0;

                while (ch & 0x80) {
                    ch <<= 1;
                    utfbytes++;
                }

                ch >>= utfbytes;

                utf8 = (int)ch;

                utfbytes--;
            }
            //continue;
            return;
        }

        switch(pcmd)
        {
            case PCMD_CURPOS_X: {
                    cur.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor);
                    cur.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor,ch);
                    setTextCursor(cur);
                    pcmd = PCMD_NONE;
                }
                break;

            case PCMD_CURPOS_Y: {
                    cur.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor);
                    cur.movePosition(QTextCursor::Down,QTextCursor::MoveAnchor,ch);
                    setTextCursor(cur);
                    pcmd = PCMD_NONE;
                }
                break;

            case PCMD_CURPOS_XY: {
                    if(pcmdlen == 2) {
                        cur.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor);
                        cur.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor,ch);
                        setTextCursor(cur);
                    }
                    else if(pcmdlen == 1) {
                        cur.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor);
                        cur.movePosition(QTextCursor::Down,QTextCursor::MoveAnchor,ch);
                        setTextCursor(cur);
                    }
                    pcmdlen--;
                    if(pcmdlen < 1)
                        pcmd = PCMD_NONE;
                }
                break;

            default: {

                switch(ch)
                {
                case EN_ClearScreen:
                case EN_ClearScreen2: {
                        if(this->enableClearScreen) {
                            setPlainText("");
                        }
                    }
                    break;

                case EN_HomeCursor: {
                        if(this->enableHomeCursor) {
                            cur.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_PosXYCursor: {
                        if(this->enablePosXYCursor) {
                            pcmd = PCMD_CURPOS_XY;
                            pcmdlen  = 2;
                        }
                    }
                    break;

                case EN_MoveCursorLeft: {
                        if(this->enableMoveCursorLeft) {
                            cur.movePosition(QTextCursor::Left,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_MoveCursorRight: {
                        if(this->enableMoveCursorRight) {
                            cur.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_MoveCursorUp: {
                        if(this->enableMoveCursorUp) {
                            cur.movePosition(QTextCursor::Up,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_MoveCursorDown: {
                        if(this->enableMoveCursorDown) {
                            cur.movePosition(QTextCursor::Down,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_BeepSpeaker: {
                        if(this->enableBeepSpeaker) {
                            QApplication::beep();
                        }
                    }
                    break;

                case EN_Backspace: {
                        if(this->enableBackspace) {
                            text = toPlainText();
                            setPlainText(text.mid(0,text.length()-1));
                            moveCursor(QTextCursor::End);
                        }
                    }
                    break;

                case EN_Tab: {
                        if(this->enableTab) {
                            int column = cur.columnNumber() % 8;
                            while(column++ < 8)
                                cur.movePosition(QTextCursor::Right,QTextCursor::MoveAnchor);
                            setTextCursor(cur);
                        }
                    }
                    break;

                /* fall through case because newline and creturn can change roles
                 * the switch is just to speed things up
                 */
                case EN_NewLine: // fall through
                case EN_CReturn: {
                        if(ch == newline) {
                            if(enableNewLine) {
                                cur.insertBlock();
                                setTextCursor(cur);
                            }
                        }
                        else if(ch == creturn) {
                            if(enableCReturn) {
                                text = toPlainText();
                                cur.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor);
                                setTextCursor(cur);
                            }
                        }
                        lastchar = ch; // later check for appendCR and add it
                    }
                    break;

                case EN_ClearToEOL: {
                        if(this->enableClearToEOL) {
                            cur.movePosition(QTextCursor::EndOfLine,QTextCursor::KeepAnchor);
                            cur.removeSelectedText();
                            setTextCursor(cur);
                        }
                    }
                    break;

                case EN_ClearLinesBelow: {
                        if(this->enableClearLinesBelow) {
                            int row = cur.blockNumber();
                            int col = cur.columnNumber();
                            cur.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor);
                            cur.movePosition(QTextCursor::Down,QTextCursor::KeepAnchor,row);
                            cur.movePosition(QTextCursor::Right,QTextCursor::KeepAnchor,col);
                            QString s = cur.selectedText();
                            this->setPlainText(s);
                        }
                    }
                    break;

                default: {
                        cur.movePosition(QTextCursor::Right,QTextCursor::KeepAnchor);
                        cur.removeSelectedText();
                        cur.insertText(QString(ch));
                        setTextCursor(cur);
                    }
                    break;
                }
                break;

            } // end pcmd default
        } // end pcmd switch
    } // end for
}

